Angular Routing and Navigation

Client-side routing (all done on the client through angular’s routing engine).

The index.html must have the <base> element

<!doctype html>
<html>
<head>
<metacharset="utf-8">
<title>Myquickstart</title>
<base href="/">
<metaname="viewport"content="width=device-width, initial-scale=1">
<linkrel="icon"type="image/x-icon"href="favicon.ico">
</head>
<body>
<app-root>Loading...</app-root>
</body>
</html>

The <base> element is recognized by the router and instructs how to compose navigation URLs.

The app.module.ts file must have the RouterModule

Import { RouterModule, Routes } from ‘@angular/router’;

The above is the ES6 import statement into the javascript class. In order for Angular to recognize it, we must add it to the imports in the decorator of the module it is getting imported at. Since routing is for the overall application, we do this in the app.module as shown below. Note that when the path is not specified, Angular routing will catch it as a path: ‘ ‘ and will redirect it to the route specified, as shown below. Also, there is a catch all route ‘**’ where any other url will get forwarded to the specified component or route.

For routes that may have childrens, we use the ‘children’ property and define the child routes under it. As example below shows the OrgChartComponent has multiple children and they are defined under the children property.

 

  imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot([
        { path: 'org-chart', 
          component: OrgChartComponent,
          canActivate: [ CanActivateGuard ],
          children: [
            { path: 'child1', component: Child1Component },
            { path: 'child2', component: Child2Component },
            { path: '**', component: SettingsComponent }
          ] },
        { path: 'employee-list', component: EmployeeListComponent},
        { path: 'employee/:id', component: EmployeeComponent },
        { path: 'settings', component: SettingsComponent },
        { path: '', redirectTo: '/org-chart', pathMatch: 'full' },
        { path: '**', component: SettingsComponent }
      ]
    )
  ],

Note that in order for Angular to load the components referenced in the routes, a <router-outlet> must be added to the main app.component. The example below also shows how to setup a button to link to a route by using the routerLinks. If we want to show the current active route, we can add CSS to show it is selected. This styling can be done using the routerLinkActive directive. The “active-route” is a CSS class name that gets applied to the button when it is active.

@Component({
  selector: 'app-root',
  template: `
    <button routerLink="/org-chart"
            routerLinkActive="active-route" >Org Chart</button>
    <button routerLink="/org-chart"
            routerLinkActive="active-route">Employee List</button>
    <button routerLink="/org-chart"
            routerLinkActive="active-route">Settings</button>
    <router-outlet></router-outlet>
`,
styleUrls: ['./app.component.css']
})

 

Router Service

Within a component we can call the Router service to make route calls. To do this we need to import the ActivatedRoute and Router classes as the ES6 import statement on top of the component class. For Angular to recognize these services we need to inject them (DI) in the constructor.

To navigate to some route, we can use the router service and call the navigate method. This is shown in the example below

this.router.navigate(['employee', '5']);

To access the current url, we can use the activatedRoute service and call the url method, which returns an observable. So subscribe to it and when we hit the onclick, it will display the current url.

this.activatedRoute.url.subscribe(segments => console.log(segments));

The complete example is shown below

import { Component } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';

@Component({
  selector: 'app-employee-list',
  template: `
  <h1>Employee List</h1>
  <button (click)="onClick()">Employee 5</button>
  `,
  styles: []
})
export class EmployeeListComponent {

  constructor(private activatedRoute: ActivatedRoute,
              private router: Router) { }

  onClick() {
    //this.router.navigate(['employee', '5']);
    this.activatedRoute.url.subscribe(segments => console.log(segments));
  }
}

 

To access a parameter in the current route, we can use the paramMap method under the activaatedRoute service. This is also an observable so we need to subscribe to it. The example below gets the param with key = id. To get all parameter keys in the current url, we can call the keys method on the service.

paramMap.has('id') // checks if id key exists
paramMap.keys; // returns all the keys as an array

The complete example:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';

@Component({
  selector: 'app-employee',
  template: `
   <h1>Employee</h1>
   <button (click)="onClick()">Info</button>
  `,
  styles: []
})
export class EmployeeComponent implements OnInit {

  constructor(private activatedRoute: ActivatedRoute,
              private router: Router) { }

  ngOnInit() {
    let id = this.activatedRoute.paramMap.subscribe(
      params => console.log(params.get('id')) );
  }

  onClick() {
    this.router.navigate(['employee', '77']);
  }
}

 

The Activated Route service has several other properties that give information about the route path and parameters.

  • url = An Observable of the route path(s), represented as an array of strings for each part of the route path.
  • data = An Observable that contains the data object provided for the route. Also contains any resolved values from the resolve guard.
  • paramMap = An Observable that contains a map of the required and optional parameters specific to the route. The map supports retrieving single and multiple values from the same parameter.
  • queryParamMap = An Observable that contains a map of the query parameters available to all routes. The map supports retrieving single and multiple values from the query parameter.
  • fragment = An Observable of the URL fragment available to all routes.
  • outlet = The name of the RouterOutlet used to render the route. For an unnamed outlet, the outlet name is primary.
  • routeConfig = The route configuration used for the route that contains the origin path.
  • parent = The route’s parent ActivatedRoute when this route is a child route.
  • firstChild = Contains the first ActivatedRoute in the list of this route’s child routes.
  • children = Contains all the child routes activated under the current route.

 

Router Events

The Router service emits navigation events through the Router.events property. These includes events when navigation starts, ends, and any point in between. To get a full trace of these events, set the enableTracing option on to see them in the console. A full list of events can be found here.

https://angular.io/guide/router#!#child-routing-component

 

Router Guards

A route guard puts rules around a route before it is executed. For example, check if the route can be activated. This can be done with canActivate, which was shown in the first example above.

canActivate: [ CanActivateGuard ],

The CanActivateGuard is a custom class created to filter the routes. The complete code is shown below.

import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';

export class CanActivateGuard implements CanActivate {

    canActivate(route: ActivatedRouteSnapshot,
                state: RouterStateSnapshot) 
                  : Observable|Promise|boolean {
      return true;
    }
}

Likewise, if we want to test if a route can be deactivated, we have the same syntax as above but call canDeactivate property. Other Angular built-in router guards are Resolve and CanLoad.